import {
  ContactState,
  DirectionType,
  ContactEventType,
  ContactErrorType
} from '../../common/commlib.enums';
import { AWSDevice } from './aws-device';
import { Contact } from '../../services/contact';
import { IMessageParams } from '../../common/commlib.interfaces';

export class AWSContact extends Contact {
  // Also record aws device directly to call aws specified API
  constructor(
    awsContact: any,
    awsdevice: AWSDevice,
    initializeState?: ContactState
  ) {
    super(awsContact.getContactId(), awsdevice, awsContact);
    if (initializeState) {
      this.state = initializeState;
    }
    this.subscriptContactEvents(awsContact);
    this.initializeProperties(awsContact);
  }

  public accept(): any {
    this.rawContact.accept({
      success: () => {
        console.log('Accept contact successfully - server received your cmd');
      },
      failure: () => {
        console.error('call awscontact.accept failed');
        this.onError$.next({
          type: ContactErrorType.JOIN_CONTACT_FAILED,
          message: 'call awscontact.accept failed',
          payload: this
        });
      }
    });
    return this;
  }

  public getDirection() {
    return this.rawContact.isInbound()
      ? DirectionType.INBOUND
      : DirectionType.OUTBOUND;
  }

  public decline(): any {
    this.hangup();
  }

  public mute(): any {
    (this.device as AWSDevice).mute();
    this.addState(ContactState.MUTE);
    return this;
  }

  public unMute(): any {
    (this.device as AWSDevice).unmute();
    this.removeState(ContactState.MUTE);
    return this;
  }

  public hold(): any {
    this.rawContact.getInitialConnection().hold({
      success: () => {
        this.state = this.addState(ContactState.HOLD);
        this.onNewEvent$.next({
          eventType: ContactEventType.CHANGE,
          payload: this
        });
      },
      failure: () => {
        console.error('hold failed');
        this.onError$.next({
          type: ContactErrorType.HOLD_FAILED,
          message: 'call awscontact.hold failed',
          payload: this
        });
      }
    });
    return this;
  }

  public unHold(): any {
    this.rawContact.getInitialConnection().resume({
      success: () => {
        this.state = this.removeState(ContactState.HOLD);
        this.onNewEvent$.next({
          eventType: ContactEventType.CHANGE,
          payload: this
        });
      },
      failure: () => {
        console.error('resume failed');
        this.onError$.next({
          type: ContactErrorType.RESUME_FAILED,
          message: 'call awscontact.resume failed',
          payload: this
        });
      }
    });
    return this;
  }

  public hangup(): any {
    this.rawContact.getAgentConnection().destroy({
      success: () => {
        this.endedBy = 'agent';
        console.log('hangup is executed');
      },
      failure: () => {
        console.error('failed');
        this.onError$.next({
          type: ContactErrorType.HANGUP_FAILED,
          message: 'call awscontact.destroy failed',
          payload: this
        });
      }
    });
    return this;
  }

  public sendMessage(_messageParams: IMessageParams): any {
    //
  }

  public getPreviousMessages(_params: any): any {
    return new Promise(resolve => {
      resolve();
    });
  }

  public typing(_parameters: any): any {
    //
  }

  private addState(addedState: ContactState): ContactState {
    switch (addedState) {
      case ContactState.HOLD:
        this.state =
          this.state === ContactState.MUTE
            ? ContactState.HOLDANDMUTE
            : ContactState.HOLD;
        break;

      case ContactState.MUTE:
        this.state =
          this.state === ContactState.HOLD
            ? ContactState.HOLDANDMUTE
            : ContactState.MUTE;
        break;

      default:
        break;
    }
    return this.state;
  }

  private removeState(removedState: ContactState): ContactState {
    switch (removedState) {
      case ContactState.HOLD:
        this.state =
          this.state === ContactState.HOLDANDMUTE
            ? ContactState.MUTE
            : ContactState.CONNECTED;
        break;

      case ContactState.MUTE:
        this.state =
          this.state === ContactState.HOLDANDMUTE
            ? ContactState.HOLD
            : ContactState.CONNECTED;
        break;

      default:
        break;
    }
    return this.state;
  }

  private subscriptContactEvents(contact: any) {
    contact.onConnected((newcontact: any) => {
      if (newcontact.getContactId() !== this.id) {
        return;
      }
      this.connectedAt = new Date();
      this.state = ContactState.ACCEPTED;
      this.onNewEvent$.next({
        eventType: ContactEventType.CHANGE,
        payload: this
      });
      this.state = ContactState.CONNECTED;
      this.onNewEvent$.next({
        eventType: ContactEventType.CHANGE,
        payload: this
      });
    });

    contact.onEnded((newcontact: any) => {
      if (newcontact.getContactId() !== this.id) {
        return;
      }
      this.endedAt = new Date();
      this.state = ContactState.ENDED;
      this.onNewEvent$.next({
        eventType: ContactEventType.CHANGE,
        payload: this
      });
    });

    contact.onMissed((newcontact: any) => {
      if (newcontact.getContactId() !== this.id) {
        return;
      }
      this.state = ContactState.ENDED;
      this.onNewEvent$.next({
        eventType: ContactEventType.CHANGE,
        payload: this
      });
    });
  }

  private initializeProperties(awsContact: any) {
    this.queueName = awsContact.getQueue().name || 'unknown queue';
    if (awsContact.getActiveInitialConnection()) {
      this.fromAddress = awsContact
        .getActiveInitialConnection()
        .getEndpoint().phoneNumber;
      this.createdAt = awsContact
        .getActiveInitialConnection()
        .getStatus().timestamp;
    }
    this.createdBy =
      awsContact.getActiveInitialConnection().getType() === 'inbound'
        ? this.fromAddress
        : 'agent';
  }
}
